use actix_session::Session;
use actix_web::{
    web::{self, ServiceConfig},
    HttpRequest, HttpResponse,
};

use crate::{
    errors::cons_error,
    models::user::{NormalUserModel, UserModelDTO},
    services::user_service::{AccountPassowrdLoginReq, UserService},
    AppError, AppResult, ArcConfig,
};
use crate::{
    models::account::PlatformUserInfo,
    services::{gitee_service::GiteeService, github_service::GithubService},
};

use super::OK_RESPONSE;

mod ctrl_struct {
    use serde::{Deserialize, Serialize};
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct UpdatePasswordCtrlReq {
        pub password: String,
    }
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct GiteeCodeReq {
        pub code: String,
    }
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct GithubCallbackQueryParam {
        pub code: String,
    }
    define_request_struct!(GiteeLoginReq {
        redirect_uri: String
    });
    define_request_struct!(GithubLoginReq {
        redirect_uri: String
    });
}

async fn account_password_login(
    user_service: web::Data<UserService>,
    session: Session,
    req: web::Json<AccountPassowrdLoginReq>,
    h_req: HttpRequest,
) -> AppResult<HttpResponse> {
    let req = req.into_inner();
    let real_ip = {
        h_req
            .connection_info()
            .realip_remote_addr()
            .map(ToOwned::to_owned)
    };
    let user = user_service.account_password_login(&req, real_ip).await?;
    UserService::set_user_to_session(&session, &user)?;
    Ok(HttpResponse::Ok().json(UserModelDTO::from(user)))
}
/// ## get_user_info
/// 获取用户信息
/// `/api/authorize/self` `[get]`
async fn get_user_info(session: Session) -> AppResult<HttpResponse> {
    let user = UserService::get_user_from_session(&session)?;
    Ok(HttpResponse::Ok().json(UserModelDTO::from(user)))
}

/// ## logout
/// 用户注销
/// `/api/authorize/self` `[delete]`
async fn logout(session: Session) -> AppResult<HttpResponse> {
    UserService::remove_user_from_session(&session)?;
    Ok(HttpResponse::Ok().json(OK_RESPONSE))
}

/// ## update_password
/// 更新用户密码
/// `/api/authorize/self/ap` `[put]`
async fn update_password(
    user_service: web::Data<UserService>,
    user: NormalUserModel,
    req: web::Json<ctrl_struct::UpdatePasswordCtrlReq>,
) -> AppResult<HttpResponse> {
    let account = user_service.get_user_account(user.id).await?;
    log::info!("account :{:?}", account);
    let password = req.into_inner().password;
    user_service
        .update_password(
            account.id.ok_or(AppError::ConsError(cons_error::MSG_MDB))?,
            password,
        )
        .await?;
    Ok(HttpResponse::Ok().json(OK_RESPONSE))
}
async fn gitee_login(
    svr: web::Data<GiteeService>,
    cfg: web::Data<ArcConfig>,
    session: Session,
    req: web::Query<ctrl_struct::GiteeLoginReq>,
) -> AppResult<HttpResponse> {
    let url = svr.get_login_url(format!(
        "{}/api/v2/authorize/gitee/callback",
        cfg.app.base_path
    ))?;
    let redirect_uri = req.into_inner().redirect_uri;
    log::info!("gitee set redirect_uri: {}", redirect_uri);
    session.insert("gitee_redirect_url", redirect_uri)?;
    Ok(HttpResponse::Found()
        .insert_header((actix_web::http::header::LOCATION, url))
        .finish())
}
async fn gitee_callback(
    svr: web::Data<GiteeService>,
    user_svr: web::Data<UserService>,
    req: web::Query<ctrl_struct::GiteeCodeReq>,
    cfg: web::Data<ArcConfig>,
    session: Session,
    h_req: HttpRequest,
) -> AppResult<HttpResponse> {
    let access_token = svr
        .get_access_token_from_code(
            &req.code,
            &format!("{}/api/v2/authorize/gitee/callback", cfg.app.base_path),
        )
        .await?;
    let user_info = svr.get_user_info_from_access_token(access_token).await?;
    let real_ip = {
        h_req
            .connection_info()
            .realip_remote_addr()
            .map(ToOwned::to_owned)
    };
    let user = user_svr
        .platform_login_or_create(PlatformUserInfo::Gitee(user_info), real_ip)
        .await?;
    UserService::set_user_to_session(&session, &user)?;
    let redirect_uri: String = session.get("gitee_redirect_url")?.unwrap_or_default();
    session.remove("gitee_redirect_url");
    log::info!("gitee redirect_uri: {}", redirect_uri);
    Ok(HttpResponse::Found()
        .insert_header((actix_web::http::header::LOCATION, redirect_uri))
        .finish())
}
async fn github_login(
    svr: web::Data<GithubService>,
    cfg: web::Data<ArcConfig>,
    session: Session,
    req: web::Query<ctrl_struct::GithubLoginReq>,
) -> AppResult<HttpResponse> {
    let login_uri = svr.get_login_url(format!(
        "{}/api/v2/authorize/github/callback",
        cfg.app.base_path
    ))?;
    let redirect_uri = req.into_inner().redirect_uri;
    log::info!("github set redirect_uri: {}", redirect_uri);
    session.insert("github_redirect_url", redirect_uri)?;
    Ok(HttpResponse::Found()
        .insert_header((actix_web::http::header::LOCATION, login_uri))
        .finish())
}
async fn github_callback(
    svr: web::Data<GithubService>,
    user_svr: web::Data<UserService>,
    session: Session,
    param: web::Query<ctrl_struct::GithubCallbackQueryParam>,
    h_req: HttpRequest,
) -> AppResult<HttpResponse> {
    let access_token = svr
        .get_access_token_from_code(param.into_inner().code)
        .await?;
    let u = svr.get_user_info_from_access_token(access_token).await?;
    let real_ip = {
        h_req
            .connection_info()
            .realip_remote_addr()
            .map(ToOwned::to_owned)
    };
    let user = user_svr
        .platform_login_or_create(PlatformUserInfo::Github(u), real_ip)
        .await?;
    UserService::set_user_to_session(&session, &user)?;
    let redirect_uri: String = session.get("github_redirect_url")?.unwrap_or_default();
    session.remove("github_redirect_url");
    log::info!("github redirect_uri: {}", redirect_uri);
    Ok(HttpResponse::Found()
        .insert_header((actix_web::http::header::LOCATION, redirect_uri))
        .finish())
}
pub fn configure_authorize_controller(config: &mut ServiceConfig) {
    config
        .service(
            web::scope("/self")
                .route("/info", web::post().to(get_user_info))
                .route("/logout", web::post().to(logout)),
        )
        .service(
            web::scope("/ap")
                .route("/login", web::post().to(account_password_login))
                .route("/update", web::post().to(update_password)),
        )
        .service(
            web::scope("/gitee")
                .route("login", web::get().to(gitee_login))
                .route("callback", web::get().to(gitee_callback)),
        )
        .service(
            web::scope("/github")
                .route("login", web::get().to(github_login))
                .route("callback", web::get().to(github_callback)),
        );
}
